package com.waves.netty.nio;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * Java NIO导出方法
 * @author dlw
 */
public class ExcelNioExporter<T> {
    /**
     * 缓冲区大小调整为 8192 字节
     */
    private static final int BUFFER_SIZE = 1024 * 8;
    private final List<List<T>> data;
    private final String fileName;
    private final int columnCount;
    private final String[] headers;
    private final CellValueFormatter<T>[] formatters;

    /**
     * 初始化方法
     * @param data
     * @param fileName
     * @param clazz
     */
    public ExcelNioExporter(List<List<T>> data, String fileName, Class<T> clazz) {
        this.data = data;
        this.fileName = fileName;
        Field[] fields = clazz.getDeclaredFields();
        List<String> columnList = new ArrayList<>();
        for (Field field : fields) {
            FieldName myField2 = field.getAnnotation(FieldName.class);
            if (myField2!=null) {
                columnList.add(myField2.columnName());
            }
        }
        String[] headers = columnList.toArray(new String[0]);
        this.headers = headers;
        this.columnCount = headers.length;
        this.formatters = new CellValueFormatter[]{
                new NoopCellValueFormatter()
        };
    }

    /***
     * 导出
     * @throws IOException
     */
    public void export() throws IOException {
        try (FileOutputStream fos = new FileOutputStream(fileName);
             FileChannel channel = fos.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
            // 写入 UTF-8 BOM
            byte[] bom = {(byte)0xEF, (byte)0xBB, (byte)0xBF};
            buffer.put(bom);
            // 写入表头
            StringBuilder sb = new StringBuilder();
            for (String header : headers) {
                sb.append(header).append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append("\r\n");
            byte[] bytes = sb.toString().getBytes("UTF-8");
            if (buffer.remaining() < bytes.length) {
                flushBuffer(buffer, channel);
            }
            buffer.put(bytes);
            // 遍历数据列表并将每行写入缓冲区
            for (List<T> rowData : data) {
                sb.setLength(0);
                T value = rowData.get(0);
                Class<?> aClass = value.getClass();
                Field[] declaredFields = aClass.getDeclaredFields();
                for (int i = 0; i < columnCount; i++) {
                    Field declaredField = declaredFields[i];
                    declaredField.setAccessible(true);
                    //获取属性的名字
                    String name = declaredField.getName();
                    name = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
                    //整合出 get() 属性这个方法
                    Method m = null;
                    Object property=null;
                    try {
                        m = aClass.getMethod("get"+name);
                        property = m.invoke(value);
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                    //时间类型的转换建议入参list中就已转换，此处也可以转
                    String formattedValue = formatters[0].format((T) property);
                    sb.append(formattedValue).append(",");
                }
                sb.deleteCharAt(sb.length() - 1);
                sb.append("\r\n");
                bytes = sb.toString().getBytes("UTF-8");
                if (buffer.remaining() < bytes.length) {
                    flushBuffer(buffer, channel);
                }
                buffer.put(bytes);
            }
            if (buffer.position() > 0) {
                flushBuffer(buffer, channel);
            }
        }
    }

    private void flushBuffer(ByteBuffer buffer, FileChannel channel) throws IOException {
        buffer.flip();
        channel.write(buffer);
        buffer.clear();
    }

    public interface CellValueFormatter<T> {
        String format(T value);
    }
}
